package com.hzw.saas.web.admin.sys.controller;

import java.awt.*;
import java.io.IOException;
import java.util.List;
import java.util.*;
import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.github.hui.quick.plugin.qrcode.wrapper.QrCodeGenWrapper;
import com.github.hui.quick.plugin.qrcode.wrapper.QrCodeOptions;
import com.google.zxing.WriterException;
import com.hzw.saas.api.property.IPropertyService;
import com.hzw.saas.api.property.enmus.SysDictKeyEnum;
import com.hzw.saas.api.rbac.IUserRoleService;
import com.hzw.saas.api.rbac.enums.PermSysEnum;
import com.hzw.saas.api.sys.dto.SysUserAccountDto;
import com.hzw.saas.api.sys.service.ISysUserService;
import com.hzw.saas.api.user.ICustomerService;
import com.hzw.saas.api.user.IStaffService;
import com.hzw.saas.api.user.bo.StaffBO;
import com.hzw.saas.common.config.annotation.SysLog;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.config.util.PageUtils;
import com.hzw.saas.common.security.pojo.dto.SaasUser;
import com.hzw.saas.common.security.utils.SecurityUtils;
import com.hzw.saas.common.util.AccountValidatorUtil;
import com.hzw.saas.web.admin.user.dto.StaffInfoDto;
import com.hzw.saas.web.admin.user.param.StaffParam;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiOperationSort;
import io.swagger.annotations.ApiParam;
import jodd.util.Base64;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;

/**
 * <p>
 *
 * </p>
 *
 * @author sonam
 * @since 2021/4/23 14:55
 */
@Controller
@RequiredArgsConstructor
@Slf4j
@Api(tags = "工作人员管理")
@RequestMapping("/sys/staff")
public class StaffController {

    private static final String INITIAL_PWD = "123456";

    private final IStaffService staffService;
    private final ISysUserService sysUserService;
    private final IUserRoleService userRoleService;

    private final ICustomerService customerService;
    private final MapperFacade mapperFacade;

    @Resource(name = IPropertyService.SYS_DICT_BEAN)
    private IPropertyService<SysDictKeyEnum> propertyService;

    @GetMapping("/")
    @SysLog
    @ApiOperation(value = "查询人员信息（姓名、手机号、邮箱[模糊查询]）", notes = "根据姓名、手机号、邮箱模糊查询")
    @ApiOperationSort(11)
    public ResponseEntity<IPage<StaffInfoDto>> searchStaff(
        PageParam pageParam, @ApiParam("模糊条件包括手机号、邮箱、姓名") @RequestParam(required = false, defaultValue = "") String fuzzyString) {
        StaffBO staffBO = new StaffBO();
        staffBO.setStaffPhoneNo(fuzzyString);
        staffBO.setStaffEmail(fuzzyString);
        staffBO.setStaffName(fuzzyString);
        IPage<StaffBO> staffBOIPage = staffService.likeSearchByPage(pageParam, staffBO);
        IPage<StaffInfoDto> staffInfoDtoIPage = PageUtils.convert(staffBOIPage, StaffInfoDto.class);
        // 计算该人员推广人数
        staffInfoDtoIPage.getRecords().forEach(staffInfoDto -> {
            List<String> channelIds = new ArrayList<>();
            channelIds.add(staffInfoDto.getPid());
            channelIds.add(staffInfoDto.getStaffPhoneNo());
            if (CollectionUtils.isNotEmpty(staffInfoDto.getStaffPhoneNoList()))
                channelIds.addAll(staffInfoDto.getStaffPhoneNoList());
            staffInfoDto.setChannelNum(customerService.countByChannelId(channelIds));
        });

        return ResponseEntity.ok(staffInfoDtoIPage);
    }

    @GetMapping("/{staffId}/role/ids")
    @SysLog
    @ApiOperation(value = "获取人员角色ID列表")
    @ApiOperationSort(12)
    public ResponseEntity<List<String>> listStaffRoleIds(@PathVariable String staffId) {
        StaffBO staffBO = staffService.getById(staffId);
        AssertUtil.assertThrow("人员信息未找到", Objects.isNull(staffBO));
        SysUserAccountDto sysUser = this.getSysUser(staffBO.getStaffPhoneNo(), staffBO.getStaffEmail());
        if (Objects.isNull(sysUser)) {
            this.addSysUser(staffBO.getStaffPhoneNo(), staffBO.getStaffEmail(), null);
            return ResponseEntity.ok(new ArrayList<>());
        }
        List<String> roleIds = userRoleService.listRoleIds(PermSysEnum.ADMIN.getCode(), sysUser.getUserId());
        return ResponseEntity.ok(roleIds);
    }

    @PostMapping("/")
    @Transactional(rollbackFor = Exception.class)
    @SysLog(operation = "添加人员信息", mode = 3)
    @ApiOperation(value = "添加人员信息")
    @ApiOperationSort(13)
    public ResponseEntity<Void> addStaff(@RequestBody @Validated StaffParam staffParam) {
        AssertUtil.assertThrow("添加人员信息失败，不能填写相同的手机号码", checkStaffParam(staffParam));
        AssertUtil.assertThrow("添加人员信息失败,存在相同的手机号", Objects.nonNull(staffService.queryByPhoneNo(staffParam.getStaffPhoneNo())));
        AssertUtil.assertThrow("添加人员信息失败,存在相同的邮箱信息", Objects.nonNull(staffService.queryOne(new StaffBO().setStaffEmail(staffParam.getStaffEmail()))));
        // 添加员工信息
        StaffBO staffBO = mapperFacade.map(staffParam, StaffBO.class);
        staffBO.setPid(null);
        staffService.addStaff(staffBO);
        // 新增后台用户
        this.addSysUser(staffBO.getStaffPhoneNo(), staffBO.getStaffEmail(), staffParam.getRoleIds());
        return ResponseEntity.ok().build();
    }

    @PutMapping("/")
    @Transactional(rollbackFor = Exception.class)
    @SysLog(operation = "更新人员信息", mode = 3)
    @ApiOperation(value = "更新人员信息")
    @ApiOperationSort(14)
    public ResponseEntity<Void> updateStaff(@RequestBody @Validated StaffParam staffParam) {
        AssertUtil.assertThrow("更新人员信息失败，请输入ID", StringUtils.isBlank(staffParam.getPid()));
        StaffBO oldStaff = staffService.getById(staffParam.getPid());
        AssertUtil.assertThrow("更新人员信息失败，人员信息不存在", Objects.isNull(oldStaff));
        AssertUtil.assertThrow("更新人员信息失败，不能填写相同的手机号码", checkStaffParam(staffParam));
        StaffBO staffBO = mapperFacade.map(staffParam, StaffBO.class);
        List<String> staffPhoneNoList = staffParam.getStaffPhoneNoList();
        if (CollectionUtils.isEmpty(staffPhoneNoList)) {
            staffPhoneNoList = new ArrayList<>();
            staffParam.setStaffPhoneNoList(staffPhoneNoList);
        }
        staffPhoneNoList.add(staffParam.getStaffPhoneNo());

        // 检查手机号是否存在
        for (String staffPhoneNo : staffPhoneNoList) {
            if (StringUtil.isNotBlank(staffPhoneNo)) {
                AssertUtil.assertThrow("更新人员信息失败, 手机号格式错误", !AccountValidatorUtil.isMobile(staffPhoneNo));
                StaffBO oldStaffBo = staffService.queryByPhoneNo(staffPhoneNo);
                AssertUtil.assertThrow("更新人员信息失败, 已存在手机号", Objects.nonNull(oldStaffBo) && !Objects.equals(oldStaffBo.getPid(), staffBO.getPid()));
            }
        }

        // 检查邮箱是否重复
        if (StringUtil.isNotBlank(staffParam.getStaffEmail())) {
            StaffBO oldStaffBo = staffService.queryOne(new StaffBO().setStaffEmail(staffParam.getStaffEmail()));
            AssertUtil.assertThrow("更新人员信息失败, 已存在邮箱", Objects.nonNull(oldStaffBo) && !Objects.equals(oldStaffBo.getPid(), staffBO.getPid()));
        }

        if (!Objects.equals(oldStaff, staffBO)) {
            staffService.updateById(staffBO);
        }

        // 更新后台账户
        this.updateSysUser(oldStaff.getStaffPhoneNo(), oldStaff.getStaffEmail(), staffBO.getStaffPhoneNo(), staffBO.getStaffEmail(), staffParam.getRoleIds());

        return ResponseEntity.ok().build();
    }

    @PutMapping("/{staffId}/pwd/default")
    @SysLog(operation = "重置人员后台账户密码", mode = 3)
    @ApiOperation(value = "重置人员后台账户密码")
    @ApiOperationSort(15)
    public ResponseEntity<Void> resetSysUserPassword(@PathVariable String staffId) {
        StaffBO staffBO = staffService.getById(staffId);
        AssertUtil.assertThrow("未找到人员信息", Objects.isNull(staffBO));
        SysUserAccountDto sysUser = this.getSysUser(staffBO.getStaffPhoneNo(), staffBO.getStaffEmail());
        if (Objects.isNull(sysUser)) {
            // 新增
            this.addSysUser(staffBO.getStaffPhoneNo(), staffBO.getStaffEmail(), null);
        } else {
            sysUserService.resetPassword(sysUser.getUserId(), INITIAL_PWD);
        }
        return ResponseEntity.ok().build();
    }

    @DeleteMapping("/{staffId}")
    @Transactional(rollbackFor = Exception.class)
    @SysLog(operation = "删除人员信息", mode = 3)
    @ApiOperation(value = "删除人员信息")
    @ApiOperationSort(16)
    public ResponseEntity<Void> deleteStaffById(@PathVariable String staffId) {
        StaffBO staffBO = staffService.getById(staffId);
        AssertUtil.assertThrow("人员信息未找到", Objects.isNull(staffBO));
        // 删除人员信息
        staffService.deleteByIds(Collections.singletonList(staffId));
        // 删除账户信息和角色关联信息
        SysUserAccountDto sysUser = this.getSysUser(staffBO.getStaffPhoneNo(), staffBO.getStaffEmail());
        if (Objects.nonNull(sysUser)) {
            sysUserService.delete(sysUser.getUserId());
        }
        return ResponseEntity.ok().build();
    }

    @DeleteMapping("/")
    @Transactional(rollbackFor = Exception.class)
    @SysLog(operation = "批量删除人员信息", mode = 3)
    @ApiOperation(value = "批量删除人员信息")
    @ApiOperationSort(17)
    public ResponseEntity<Void> deleteStaffById(List<String> staffIds) {
        if (Objects.isNull(staffIds)) {
            return ResponseEntity.ok().build();
        }
        List<StaffBO> staffBOS = staffService.listByIds(staffIds);
        staffService.deleteByIds(staffIds);
        // 删除后台账户
        for (StaffBO staffBO : staffBOS) {
            SysUserAccountDto sysUser = this.getSysUser(staffBO.getStaffPhoneNo(), staffBO.getStaffEmail());
            if (Objects.nonNull(sysUser)) {
                sysUserService.delete(sysUser.getUserId());
            }
        }
        return ResponseEntity.ok().build();
    }

    @GetMapping("/{staffId}/qrcode")
    @SysLog(operation = "生成一个人员专属二维码", mode = 3)
    @ApiOperation(value = "生成一个人员的专属二维码")
    @ApiOperationSort(18)
    public ResponseEntity<String> generateQRCode(@PathVariable String staffId,
                                                 @ApiParam("二维码宽度") @RequestParam(required = false, defaultValue = "250") Integer width,
                                                 @ApiParam("二维码高度") @RequestParam(required = false, defaultValue = "250") Integer height) {
        try {
            StaffBO staffBO = staffService.getById(staffId);
            AssertUtil.assertThrow("生成二维码失败,人员信息不存在", Objects.isNull(staffBO));
            byte[] bytes = genQRCode(staffId, width, height);
            return ResponseEntity.ok(Base64.encodeToString(bytes));
        } catch (WriterException | IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @GetMapping("/qrcode")
    @SysLog(operation = "生成本人专属二维码", mode = 3)
    @ApiOperation(value = "生成本人专属二维码")
    @ApiOperationSort(18)
    public ResponseEntity<String> generateQRCode(@ApiParam("二维码宽度") @RequestParam(required = false, defaultValue = "250") Integer width,
                                                 @ApiParam("二维码高度") @RequestParam(required = false, defaultValue = "250") Integer height) {
        SaasUser user = SecurityUtils.getUser();
        // 手机号或者邮箱
        String userName = user.getUsername();
        List<StaffBO> staffs = staffService.query(new StaffBO().setStaffPhoneNo(userName));
        if (CollectionUtils.isEmpty(staffs)) {
            staffs = staffService.query(new StaffBO().setStaffEmail(userName));
        }
        AssertUtil.assertThrow("生成二维码失败，您的工作信息不存在", CollectionUtils.isEmpty(staffs));
        return generateQRCode(staffs.get(0).getPid(), width, height);
    }

    private byte[] genQRCode(String staffId, Integer width, Integer height) throws WriterException, IOException {
        String registerURI = propertyService.getPropertyValue(SysDictKeyEnum.REGISTER_URI, "");
        String logoUrl = propertyService.getPropertyValue(SysDictKeyEnum.LOGO_URL, "https://saas-base.s3.cn-northwest-1.amazonaws.com.cn/logo.png");
        String content = StrFormatter.format("{}?channelType={}&channelId={}", registerURI, SysDictKeyEnum.CHANNEL_C.getKey(), staffId);
        byte[] bytes = QrCodeGenWrapper
            .of(content)
            .setLogo(logoUrl)
            .setLogoStyle(QrCodeOptions.LogoStyle.ROUND)
            .setW(width)
            .setH(height)
            .setDrawPreColor(new Color(79, 138, 193))
            .asStream().toByteArray();
        return bytes;
    }

    private boolean checkStaffParam(StaffParam staffParam) {
        List<String> staffPhoneNoList = new ArrayList<>();
        // 主手机号
        staffPhoneNoList.add(staffParam.getStaffPhoneNo());
        // 副手机号
        if (CollectionUtils.isNotEmpty(staffParam.getStaffPhoneNoList())) {
            staffPhoneNoList.addAll(staffParam.getStaffPhoneNoList());
        }

        // 检查是否填写相同的手机号
        int size = staffPhoneNoList.size();
        HashSet<String> set = new HashSet<>(staffPhoneNoList);
        staffPhoneNoList.clear();
        staffPhoneNoList.addAll(set);
        return size != staffPhoneNoList.size();
    }

    private SysUserAccountDto getSysUser(String phoneNo, String email) {
        SysUserAccountDto query = new SysUserAccountDto();
        if (StrUtil.isNotBlank(phoneNo)) {
            query.setPhoneNo(phoneNo);
        } else if (StrUtil.isNotBlank(email)) {
            query.setEmail(email);
        } else {
            return null;
        }
        return sysUserService.get(query);
    }

    private SysUserAccountDto addSysUser(String phoneNo, String email, List<String> roleIds) {
        SysUserAccountDto sysUser = new SysUserAccountDto();
        if (StrUtil.isNotBlank(phoneNo)) {
            sysUser.setUserName(phoneNo);
        } else if (StrUtil.isNotBlank(email)) {
            sysUser.setUserName(email);
        } else {
            throw DefaultSaasException.build("员工邮箱手机号不可同时为空，请添加手机或邮箱").status(HttpStatus.BAD_REQUEST);
        }
        sysUser.setPassword(INITIAL_PWD);
        sysUser.setPhoneNo(phoneNo);
        sysUser.setEmail(email);
        return sysUserService.add(sysUser, roleIds);
    }

    private SysUserAccountDto updateSysUser(String oldPhoneNo, String oldEmail, String newPhoneNo, String newEmail, List<String> roleIds) {
        // 根据手机或邮箱查找后台账户
        SysUserAccountDto sysUser = this.getSysUser(oldPhoneNo, oldEmail);
        if (Objects.isNull(sysUser)) {
            // 未找到后台账户，新增
            String phoneNo = StrUtil.isNotBlank(newPhoneNo) ? newPhoneNo : oldPhoneNo;
            String email = StrUtil.isNotBlank(newEmail) ? newEmail : oldEmail;
            return this.addSysUser(phoneNo, email, roleIds);
        } else {
            // 更新后台账户
            sysUser.setPhoneNo(newPhoneNo);
            sysUser.setEmail(newEmail);
            sysUserService.update(sysUser, roleIds);
            return sysUser;
        }
    }


}
